﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Co9320.NamaAlert.Model;
using Model = Co9320.NamaAlert.Model;

namespace Co9320.NamaAlert.Engine
{
	public class LiveNotification : IDisposable, Model::ISystemMessageSender
	{
#if DEBUG
		internal LiveProgramNotification LiveProgram_;
		internal ReserveNotification ReserveProgram_;
#else
		private LiveProgramNotification LiveProgram_;
		private ReserveNotification ReserveProgram_;
#endif

		private Semaphore LimitGate_;

		private bool IsStarted_;

		private TimeSpan ReserveRaiseBefore_;

		private WaitCallback eventProcess;

		public event EventHandler<LiveProgramEventArgs> LiveProgramStarted;

		public event EventHandler<LiveProgramEventArgs> LiveProgramClosed;

		public event EventHandler<LiveProgramEventArgs> LiveProgramUpdated;

		public event EventHandler FirstCrawlingCompleted
		{
			add
			{
				LiveProgram_.FirstCrawlingComplted += value;
			}
			remove
			{
				LiveProgram_.FirstCrawlingComplted -= value;
			}
		}



		public LiveNotification(TimeSpan reserveRaiseBefore)
			:this("",
			"http://live.nicovideo.jp/recent/rss?tab=&sort=&p={0}",
			reserveRaiseBefore)
		{
		}

        public LiveNotification(string userAgent, string baseUriFormat, TimeSpan reserveRaiseBefore)
		{
			ReserveRaiseBefore_ = reserveRaiseBefore;
			this.UserAgent = userAgent;
			this.BaseUriFormat = baseUriFormat;

			eventProcess = new WaitCallback(this.SendEventProcess);
			LiveProgram_ = new LiveProgramNotification(this, baseUriFormat, UserAgent);

			LimitGate_ = new Semaphore(20, 20);
		}

		public void Start()
		{
			Start(CrawlingMode.Normal);
		}

		public void Start(CrawlingMode mode)
		{
			if (!IsStarted_)
			{
				ReserveProgram_ = new ReserveNotification(ReserveRaiseBefore_, this);
				LiveProgram_.Start(mode);
				IsStarted_ = true;
			}
		}

		public void Start(CrawlingMode mode, IList<LiveProgram> programList, bool crawlAllPages)
		{
			if (!IsStarted_)
			{
				ReserveProgram_ = new ReserveNotification(ReserveRaiseBefore_, this);
				LiveProgram_.Start(mode, programList, crawlAllPages);
				IsStarted_ = true;
			}
		}

		public void Start(IList<LiveProgram> programList,bool crawlAllPages)
		{
			Start( CrawlingMode.Normal,programList,crawlAllPages);
		}




		public bool AddProgram(LiveProgram program)
		{
			return LiveProgram_.AddProgram(program);
		}


		public string BaseUriFormat
		{
			get;
			private set;
		}

		public string UserAgent
		{
			get;
			private set;
		}

		public int CurrentProgramCount
		{
			get
			{
				if (LiveProgram_ == null)
				{
					return -1;
				}
				else
				{
					return LiveProgram_.CurrentProgramCount;
				}
			}
		}




		internal void SendStateChanged(IList<StateChangedEventArgs> eventArgList)
		{
			Debug.Assert(eventArgList != null);

			LimitGate_.WaitOne();

			ThreadPool.QueueUserWorkItem(eventProcess, eventArgList);

			LimitGate_.Release();
		}


		private void SendEventProcess(object param)
		{
			Debug.Assert(param != null);



			IList<StateChangedEventArgs> lst = (IList<StateChangedEventArgs>)param;

			for (int i = 0; i < lst.Count; i++)
			{
				switch (lst[i].State)
				{
					case InspectionResult.Ignore:
						break;

					case InspectionResult.DetectOffAir:
						OnLiveProgramClosed(this, new LiveProgramEventArgs(lst[i].Program));
						break;

					case InspectionResult.DetectOnAir:
						OnLiveProgramStarted(this, new LiveProgramEventArgs(lst[i].Program));
						break;

					default:
						if (CrawlingMode == CrawlingMode.Normal)
						{
							OnLiveProgramUpdated(this, new LiveProgramEventArgs(lst[i].Program));
						}
						break;
				}
			}
		}

		public void ChangeCrawlingMode(CrawlingMode mode)
		{
			LiveProgram_.ChangeCrawlingMode(mode);
		}

		public CrawlingMode CrawlingMode
		{
			get
			{
				return LiveProgram_.CrawlingMode;
			}
		}

		public TimeSpan ReserveAlertBefore
		{
			get
			{
				return this.ReserveProgram_.AlertBefore;
			}
			set
			{
				this.ReserveProgram_.AlertBefore = value;
			}
		}


		private void OnLiveProgramStarted(object sender, LiveProgramEventArgs e)
		{
			if (LiveProgramStarted != null)
			{
				LiveProgramStarted(sender, e);
			}
		}

		private void OnLiveProgramClosed(object sender, LiveProgramEventArgs e)
		{
			if (LiveProgramClosed != null)
			{
				LiveProgramClosed(sender, e);
			}
		}

		private void OnLiveProgramUpdated(object sender, LiveProgramEventArgs e)
		{
			if (LiveProgramUpdated != null)
			{
				LiveProgramUpdated(sender, e);
			}
		}



		#region ISystemMessageSender メンバ

		public event EventHandler<SystemMessageEventArgs> SendSystemMessage;

		private void OnSendSystemMessage(object sender, SystemMessageEventArgs e)
		{
			if (SendSystemMessage != null)
			{
				SendSystemMessage(sender, e);
			}
		}

		internal void InvokeSendSystemMessage(SystemMessageEventArgs e)
		{
			OnSendSystemMessage(this, e);
		}

		#endregion

		#region IDisposable メンバ

		public void Dispose()
		{
			this.LiveProgram_.Dispose();
		}

		#endregion
	}
}
